package com.cx.base.day14_channel.nio_02_zeroCopy.baseMmap2Zero;

/**
 * @Author: robert.song
 * @Date: Created in 2022/4/24
 */

import sun.misc.Cleaner;
import sun.nio.ch.DirectBuffer;

import java.io.*;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

/**
 * 一、Channel:用于源节点与目标节点之间的连接。在Java NIO中，负责缓冲区中数据传输，Channel本身不存储数据，因此需要配合缓冲区进行传输。
 * <p>
 * 二、Channel的实现类：
 * java.nio.channels.Channel 接口：
 * |-- FileChannel
 * |-- SocketChannel
 * |-- ServerSocketChannel
 * |-- DatagramChannel
 * <p>
 * 三、获取通道Channel
 * 1.Java针对支持通道的类提供了getChannel()方法
 * 本地IO
 * FileInputStream/FileOutputStream
 * RandomAccessFile
 * <p>
 * 网络IO:
 * Socket
 * ServerSocket
 * DatagramSocket
 * <p>
 * 2.在jdk1.7中的NIO.2针对各个通道提供了静态方法open()
 * <p>
 * 3.在jdk1.7中的NIO.2的Files工具类的newByteChannel()
 * <p>
 * 四、通道之间的数据传输
 * transferFrom()
 * transferTo()
 */

//空间回收问题
//
//        mmap映射出来的MappedByteBuffer实际上是一个DirectByteBuffer。
//
//        FileChannel并没有提供unmap方法，那mmap出来的ByteBuffer怎么回收呢？
//
//        回收和正规的DirectByteBuffer一样，利用虚幻引用。FileChannelImpl#mmap利用DirectByteBuffer的构造函数创建MappedByteBuffer时会提供一个FileChannelImpl#Unmapper的类，这个类的作用和DirectByteBuffer#Deallocator的作用一样，用于回收已经分配出去的DirectByteBuffer，只是mmap的回收逻辑和直接分配的DirectByteBuffer的回收逻辑不一样。
//        ————————————————
//        版权声明：本文为CSDN博主「一阵风清浪」的原创文章，遵循CC 4.0 BY-SA版权协议，转载请附上原文出处链接及本声明。
//        原文链接：https://blog.csdn.net/weixin_29447313/article/details/114444349

public class UseClass_MappedByteBuffer {

    public static String path = "d:\\mmap";

    public static void main(String[] args) throws IOException {
        File file = null;
        if (!(file = new File(path)).exists()) {
            file.mkdirs();
        }

        File file1 = new File(path, "1");

        RandomAccessFile randomAccessFile = new RandomAccessFile(file1, "rw");

//        设置buffer大小
        int len = 2048;

        // map方法将系统内存映射到MappedByteBuffer,MappedByteBuffer大小为2kb，那么生成的文件也是2kb
        // 映射模式：
        //   READ_ONLY：read only 对于MappedByteBuffer仅可读取，必须是输入流提供的channel
        //   PRIVATE：copy-on-write 写入时拷贝，必须是输出流提供的channel
        //   READ_WRITE：read/write 必须是RandomAccessFile的channel才能开启该模式，因为要求同时满足读写功能
        MappedByteBuffer mmap = randomAccessFile.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, len);

        FileChannel fileChannel = randomAccessFile.getChannel();

        System.out.println(mmap.isReadOnly());

        System.out.println(mmap.position());

        System.out.println(mmap.limit());

        byte[] bytes = new byte[2048];

        int offsetIndex = 0;
//      缓冲区是否有数据
        while (mmap.hasRemaining()) {
//            get会从文件获取数据，直到装满mmap,如果文件数据读完还有空余，则自动补齐（padding）
            bytes[offsetIndex++] = mmap.get();
        }

        System.out.println("[内容]：" + new String(bytes));

        System.out.println("z".getBytes()[0]);
        // 若MapMode为READ_ONLY，则修改数据报ReadOnlyBufferException，
        // 因此MapMode必须为READ_WRITE、PRIVATE
        mmap.put(1, "z".getBytes()[0]);
        // 写数据之后，JVM 退出之后会强制刷新的
        // 当然也可以强制刷新
        mmap.force();
        // 强制 clean mmap，当然也可以用通过虚引用回收，
        // 在Windows上必须执行unmap(mmap); 否则报错
        // Windows won't let us modify the file length while the file is mmapped
        // java.io.IOException: 请求的操作无法在使用用户映射区域打开的文件上执行
        unmap(mmap);
//        close
//        懒汉模式，因此直接getchannel()就可以获取channel对象
        randomAccessFile.getChannel().close();
        randomAccessFile.close();
    }

    // copy from FileChannelImpl#unmap(私有方法)
    private static void unmap(MappedByteBuffer bb) {

        Cleaner cl = ((DirectBuffer) bb).cleaner();

        if (cl != null)

            cl.clean();

    }

}